<?php
/** ABSTRACT core interface to the Hosting Reborn API functions.
 *
 * <p>For interfaces to API methods see:</p>
 * <ul>
 *   <li><a href="HR_accountapi.html">HR_accountApi</a></ll>
 *   <li><a href="HR_hostingaccountapi.html">HR_hostingaccountApi</a></ll>
 *   <li><a href="HR_notificationapi.html">HR_notificationApi</a></ll>
 *   <li><a href="HR_priceapi.html">HR_priceApi</a></ll>
 *   <li><a href="HR_serverapi.html">HR_serverApi</a></ll>
 *   <li><a href="HR_transactionapi.html">HR_transactionApi</a></ll>
 * </ul>
 *
 * @package com\hostingreborn\api
 * @link https//api.hostingreborn.com/docs/functions/
 * @abstract
 *
 *
 * @author      Jon Cram <jon@hostingreborn.com>
 * @copyright   https://api.hostingreborn.com/code/copyright/
 * @license     https://api.hostingreborn.com/code/license/
 * @version     1.0
 *
 * $Date$
 * $Revision$
 * 
 */

require_once('HR_apiExceptions.php');
require_once('HR_apiRequestUrl.class.php');
require_once('HR_apiResponse.class.php');

abstract class HR_api {

    /***************************************************************************/
    /* Attributes                                                              */
    /***************************************************************************/

    private static $requestUrl = null;

    private $apiDomain = null;
    private $apiDomainIsSecure = null;

    /***************************************************************************/
    /* Constructors                                                            */
    /***************************************************************************/

    /**
     *
     * @param string $apiDomain Optional, the API domain name.
     * @param bool $apiDomainIsSecure Optional, whether the API offers secure connections
     */
    public function __construct($apiDomain = null, $apiDomainIsSecure = null) {

        if (!is_null($apiDomain)) {
            $this->setApiDomain($apiDomain);
        }

        if (!is_null($apiDomainIsSecure)) {
            $this->setApiDomainIsSecure($apiDomainIsSecure);
        }
    }

    /***************************************************************************/
    /* Public methods                                                          */
    /***************************************************************************/

    /**
     * Read data from the API (a GET request)
     *
     * @param string $request The URL to read from.
     * @return HR_apiResponse
     */
    protected function read($request = null) {
        return $this->query($request);
    }


    /**
     * Write data to the API (a POST request)
     *
     * @param string $request The URL to write to
     * @param array $arguments An array of key:value POST arguments
     * @return HR_apiResponse
     */
    protected function write($request = null, $arguments = null) {
        if (!is_array($arguments)) {
            $arguments = array();
        }

        return $this->query($request, $arguments);
    }

    /***************************************************************************/
    /* Private methods                                                         */
    /***************************************************************************/


    /**
     *  Optionally specify the API request domain name.
     *
     * @param string $domain The domain name for the API request URL
     * @return bool
     */
    private function setApiDomain($domain = null) {
        if (!is_string($domain)) {
            $this->apiDomain = null;
            return false;
        }

        $domain = trim($domain);
        if ($domain == '') {
            $this->apiDomain = null;
            return false;
        }

        return $this->apiDomain = $domain;
    }


    /**
     * Optionally specify whether to make secure (HTTPS) requests to the API domain.
     *
     * @param bool $isSecure
     * @return bool
     */
    private function setApiDomainIsSecure($isSecure = null) {
        if (!is_bool($isSecure)) {
            $this->apiDomainIsSecure = null;
            return false;
        }

        return $this->apiDomainIsSecure = $isSecure;
    }


    protected function buildArguments($standardArguments, $fullArgumentList = NULL, $additionalArguments = NULL) {
        return array_merge($standardArguments, $this->getAdditionalArguments(), $this->getExtraFullArguments($standardArguments, $fullArgumentList));
    }


    protected function buildQueryString($standardArguments, $fullArgumentList = NULL, $additionalArguments = NULL) {
        return http_build_query($this->buildArguments($standardArguments, $fullArgumentList, $additionalArguments));
    }


    private function getAdditionalArguments($additionalArguments = NULL) {
        return (is_array($additionalArguments)) ? $additionalArguments : array();
    }
    

    private function getExtraFullArguments($standardArguments = NULL, $fullArgumentList = NULL) {        
        if (!is_array($standardArguments)) {
            array();
        }

        if (!is_array($fullArgumentList)) {
            array();
        }

        if (!isset($fullArgumentList[count($standardArguments)])) {
            return array();
        }

        if (!is_array($fullArgumentList[count($standardArguments)])) {
            return array();
        }

        return $fullArgumentList[count($standardArguments)];
    }


    /**
     * Query the API
     *
     * @param string $request
     * @param array $arguments
     * @return boolean
     */
    private function query($request = null, $arguments = null) {
        // Proceed no further if we have nothing to do
        if (!is_string($request)) {
            return null;
        }

        if (is_null($arguments)) {
            $arguments = array();
        }

        // Issue request and get server response
        $url = $this->requestUrl().$request;
        $options = $this->getStreamOptions($arguments);
        $context = stream_context_create($options);

        $serverResponse = @file_get_contents($url, false, $context);

        // Prepare response
        return new HR_apiResponse($serverResponse, $url);
    }

    /**
     * Factory method for getting the main API request URL object
     *
     * @return string
     */
    private function requestUrl() {
        if (is_null(self::$requestUrl)) {
            self::$requestUrl = new HR_apiRequestUrl($this->apiDomain, $this->apiDomainIsSecure);
        }

        return self::$requestUrl;
    }


    /**
     * Make and get the HTTP request stream options
     *
     * @return array
     */
    private function getStreamOptions($arguments = NULL) {
        $options = array();

        // HTTP options
        // -> request method, headers and payload
        $body = $this->getRequestBody($arguments);

        $options['http'] = array(
                'method' => $this->determineRequestMethod($arguments),
                'header' => "Content-type: application/x-www-form-urlencoded\r\n".
                        "Content-length: ".strlen($body)."\r\n".
                        "Connection: close",
                'content' => $body
        );

        // HTTPS options (if relevant)
        if ($this->requestUrl()->isSecure()) {
            $options['https'] = array(
                    'allow_self_signed'=>'true',
                    'verify_peer'=>'false'
            );
        }

        return $options;
    }

    /**
     * Get outbound request body, made from the request arguments
     *
     * @param array $arguments
     * @return string
     *
     */
    private function getRequestBody($arguments = array()) {
        return http_build_query($arguments);
    }


    /**
     * Determine request type (GET or POST) from request arguments array
     *
     * @param array $arguments
     * @return string
     *
     */
    private function determineRequestMethod($arguments = array()) {
        if (!is_array($arguments)) {
            return 'GET';
        }

        return (count($arguments) === 0) ? 'GET' : 'POST';
    }

}

?>